package clue.players.ai;

import java.util.ArrayList;
import java.util.HashSet;
import java.util.Random;
import java.util.Set;

import org.javatuples.Triplet;

import com.sun.xml.internal.ws.policy.privateutil.PolicyUtils.Collections;

import clue.cards.Card;
import clue.cards.RoomCard;
import clue.cards.SuspectCard;
import clue.cards.WeaponCard;
import clue.engine.GameState;
import clue.enums.Room;
import clue.enums.Suspect;
import clue.enums.Weapon;
import clue.players.Player;

public class DumbAIPlayer extends Player {
	HashSet<Room> possibleRooms;
	HashSet<Suspect> possibleSuspects;
	HashSet<Weapon> possibleWeapons;
	HashSet<Room> ruledOutRooms;
	HashSet<Suspect> ruledOutSuspects;
	HashSet<Weapon> ruledOutWeapons;
	Random random;
	
	public DumbAIPlayer() {
		random = new Random();
	}
	
	@Override
	public void gameStarted(GameState gs) {
		possibleRooms = new HashSet<>();
		possibleSuspects = new HashSet<>();
		possibleWeapons = new HashSet<>();
		ruledOutRooms = new HashSet<>();
		ruledOutSuspects = new HashSet<>();
		ruledOutWeapons = new HashSet<>();
		Set<Card> hand = gs.getOwnHand(this);
		for (Room r : Room.values()) {
			if (hand.contains(new RoomCard(r))) {
				ruledOutRooms.add(r);
			} else {
				possibleRooms.add(r);
			}
		}
		for (Suspect s : Suspect.values()) {
			if (hand.contains(new SuspectCard(s))) {
				ruledOutSuspects.add(s);
			} else {
				possibleSuspects.add(s);
			}
		}
		for (Weapon w : Weapon.values()) {
			if (hand.contains(new WeaponCard(w))) {
				ruledOutWeapons.add(w);
			} else {
				possibleWeapons.add(w);
			}
		}
	}

	@Override
	public Room move(GameState gs) {
		ArrayList<Room> rooms = new ArrayList<>();
		Room current = gs.IDToLocation.get(gs.getOwnID(this));
		for (Room r : Room.values()) {
			if (r.equals(current) || ruledOutRooms.contains(r))
				continue;
			rooms.add(r);
		}
		if (rooms.isEmpty())
			return (Room)ruledOutRooms.toArray()[random.nextInt(ruledOutRooms.size())];
		return rooms.get(random.nextInt(rooms.size()));
	}
	
	private Triplet<Room, Suspect, Weapon> suggestion;

	@Override
	public Triplet<Room, Suspect, Weapon> suggest(GameState gs) {
		Weapon weapon = null;
		Weapon[] weapons = new Weapon[possibleWeapons.size()];
		possibleWeapons.toArray(weapons);
		weapon = weapons[random.nextInt(possibleWeapons.size())];
		Suspect suspect = null;
		Suspect[] suspects = new Suspect[possibleSuspects.size()];
		possibleSuspects.toArray(suspects);
		suspect = suspects[random.nextInt(possibleSuspects.size())];
		suggestion = new Triplet<Room, Suspect, Weapon>(gs.IDToLocation.get(gs.getOwnID(this)), suspect, weapon);
		failedRefutationCount = 0;
		return suggestion;
	}

	@Override
	public Card refute(GameState gs, Integer suggestingPlayerID,
			Triplet<Room, Suspect, Weapon> suggestion) {
		ArrayList<Card> refutes = new ArrayList<>();
		for (Card c : gs.getOwnHand(this)) {
			if (c instanceof RoomCard) {
				RoomCard rc = (RoomCard) c;
				if (rc.getRoom().equals(suggestion.getValue0()))
					refutes.add(rc);
			} else if (c instanceof SuspectCard) {
				SuspectCard sc = (SuspectCard) c;
				if (sc.getSuspect().equals(suggestion.getValue1()))
					refutes.add(sc);
			} else if (c instanceof WeaponCard) {
				WeaponCard wc = (WeaponCard) c;
				if (wc.getWeapon().equals(suggestion.getValue2()))
					refutes.add(wc);
			}
		}
		if (refutes.size() > 0) {
			return refutes.get(random.nextInt(refutes.size()));
		} else {
			return null;
		}
	}

	@Override
	public void informMove(GameState gs, Integer movingPlayerID, Room room) {
	}

	@Override
	public void informSuggestion(GameState gs, Integer suggestingPlayerID,
			Triplet<Room, Suspect, Weapon> suggestion) {
	}

	private int failedRefutationCount;
	@Override
	public void informFailureToRefute(GameState gs, Integer failedPlayerID,
			Triplet<Room, Suspect, Weapon> suggestion) {
		failedRefutationCount++;
	}

	@Override
	public void informRefutation(GameState gs, Integer refutingPlayerID,
			Card c) {
		if (c instanceof RoomCard) {
			RoomCard rc = (RoomCard) c;
			possibleRooms.remove(rc.getRoom());
			ruledOutRooms.add(rc.getRoom());
		} else if (c instanceof SuspectCard) {
			SuspectCard sc = (SuspectCard) c;
			possibleSuspects.remove(sc.getSuspect());
			ruledOutSuspects.add(sc.getSuspect());
		} else if (c instanceof WeaponCard) {
			WeaponCard wc = (WeaponCard) c;
			possibleWeapons.remove(wc.getWeapon());
			ruledOutWeapons.add(wc.getWeapon());
		}
	}

	@Override
	public void informRefutationHidden(GameState gs, Integer refutingPlayerID,
			Integer suggestingPlayerID,
			Triplet<Room, Suspect, Weapon> suggestion) {
	}

	@Override
	public void gameEnded(GameState gs, Integer winnerID,
			Triplet<Room, Suspect, Weapon> winningAccusation) {
		// TODO Auto-generated method stub

	}

	@Override
	public void informFailedAccusation(GameState gs, Integer failedPlayerID,
			Triplet<Room, Suspect, Weapon> failedAccusation) {
	}

	@Override
	public Triplet<Room, Suspect, Weapon> accuse(GameState gs) {
		if (failedRefutationCount == gs.IDToLocation.size() - 1)
			return suggestion;
		//System.out.println(gs.getOwnID(this) + ":" + possibleRooms.size() + "," + possibleWeapons.size() + "," + possibleSuspects.size());
		if (possibleRooms.size() == 1 && possibleWeapons.size() == 1 && possibleSuspects.size() == 1) {
			return new Triplet<Room, Suspect, Weapon>((Room)possibleRooms.toArray()[0], (Suspect)possibleSuspects.toArray()[0], (Weapon)possibleWeapons.toArray()[0]);
		}
		return null;
	}

}
